package com.hczt.fire.server.common.shiro;

import com.hczt.fire.entity.sys.SysPermission;
import com.hczt.fire.entity.sys.SysRole;
import com.hczt.fire.entity.sys.SysUser;
import com.hczt.fire.server.backend.sys.service.SysUserService;
import com.hczt.fire.server.common.util.EhCacheUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Resource;

/**
 * 把今天最好的表现当作明天最新的起点．．～
 * いま 最高の表現 として 明日最新の始発．．～
 * Today the best performance  as tomorrow newest starter!
 *
 * @author marvin.ma
 * @create 2017-10-20 20:22
 * @desc 身份校验核心类，包括认证和授权
 **/
public class AuthenticationRealm extends AuthorizingRealm {

    private static final Logger log = LoggerFactory.getLogger(AuthenticationRealm.class);

    @Resource
    private SysUserService sysUserService;

    /**
     * 认证  校验用户身份是否合法
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        log.info("##################执行Shiro权限认证##################");
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;

        SysUser sysUser = null;
        try {
            sysUser = sysUserService.findByUserName(token.getUsername());
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (null == sysUser) {
            //没有找到账号
            throw new UnknownAccountException();
        }
        if (sysUser.getIsLock()) {
            //账号被锁定
            throw new LockedAccountException();
        }

        //将此用户存放到登录认证info中，无需自己做密码对比，Shiro使用CredentialsMatcher会为我们进行密码对比校验
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                sysUser, sysUser.getPassword(), getName());
        return authenticationInfo;
    }

    /**
     * 授权  查询当前用户具有哪些资源访问权限并进行资源授权
     * 此方法调用  hasRole,hasPermission的时候才会进行回调.
     * 1、如果用户正常退出，缓存自动清空；
     * 2、如果用户非正常退出，缓存自动清空；
     * 3、如果我们修改了用户的权限，而用户不退出系统，修改的权限无法立即生效。（需要手动编程进行实现；放在service进行调用）
     * 在权限修改后调用realm中的方法，realm已经由spring管理，所以从spring中获取realm实例，调用clearCached方法；
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        /*
        * 当没有使用缓存的时候，不断刷新页面的话，这个代码会不断执行，
        * 当其实没有必要每次都重新设置权限信息，所以我们需要放到缓存中进行管理；
        * 当放到缓存中时，这样的话，doGetAuthorizationInfo就只会执行一次了，
        * 缓存过期之后会再次执行。
        */
        log.info("=========执行授权===========");
        SysUser sysUser = (SysUser) principalCollection.getPrimaryPrincipal();

        //查到权限数据，返回授权信息(要包括 上边的permissions)
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

        for(SysRole role : sysUser.getSysRoles()){

            simpleAuthorizationInfo.addRole(role.getPerms());
            System.out.println(role.getSysPermissions());
            for(SysPermission p:role.getSysPermissions()){
                System.out.println(p);
                simpleAuthorizationInfo.addStringPermission(p.getPerms());
            }
        }
        return simpleAuthorizationInfo;

    }


    /**
     * 清除当前用户认证缓存信息
     */
    public void clearCachedAuthenticationInfo() {
        PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
        super.clearCachedAuthenticationInfo(principals);
    }

    /**
     * 清除当前用户授权缓存信息
     */
    public void clearCachedAuthorizationInfo() {
        PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
        super.clearCachedAuthorizationInfo(principals);
    }

    /**
     * 清除当前用户认证和授权缓存信息
     */
    public void clearCache() {
        PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
        super.clearCache(principals);
    }

    /**
     * 清除所有用户授权缓存信息
     */
    public void clearCachedAuthorizationInfoAll(){
        String cacheName = super.getAuthorizationCacheName();
        EhCacheUtils.removeAll(cacheName);
    }

}
